package it.polimi.jita.cp.block.co;

import it.polimi.jita.cp.block.Block;
import it.polimi.jita.cp.block.de.DemandEstimator;
import it.polimi.jita.cp.block.de.DemandEstimatorOutput;
import it.polimi.jita.cp.block.pc.PenaltyCalculator;
import it.polimi.jita.cp.block.pc.PenaltyCalculatorInput;
import it.polimi.jita.cp.block.pc.PenaltyOutput;
import it.polimi.jita.cp.block.pc.PenaltyType;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.csv.CSVRecord;
import org.jfree.chart.JFreeChart;

public class CapacityOptimator extends
		Block<CapacityOptimizatorInput, CapacityOptimizatorOutput> {

	private static final Double PRECISION = 0.01;

	private DemandEstimator capacityEstimator = new DemandEstimator();
	private PenaltyCalculator penaltyCalculator = new PenaltyCalculator();

	@Override
	protected CapacityOptimizatorOutput computeOutput() {
		CapacityOptimizatorInput input = getInput();

		DemandEstimatorOutput demandEstimatorOutput = estimate();
		List<Double> capacityDemand = demandEstimatorOutput
				.getDemandEstimationDbl();

		double maxCapacityDemand = demandEstimatorOutput
				.getMaxCapacityDemand();
		double systemCapacity;

		double penalty = 0.0d;
		double deltaPenalty;
		double maxPenalty = input.getMaxPenalty();

		double sInterval = 0.0d;
		double eInterval = maxCapacityDemand;

		PenaltyOutput penaltyOutput = null;
		boolean foundCapacity = false;
		do {

			systemCapacity = (eInterval + sInterval) / 2.0d;
			try {
				penaltyOutput = getPenalty(capacityDemand, systemCapacity);
			} catch (Exception e) {
				sInterval = systemCapacity;
				continue;
			}

			if (input.getPenaltyType() == PenaltyType.ASYMPTOTIC)
				penalty = penaltyOutput.getAvgAsymptoticPenalty();
			else
				penalty = penaltyOutput.getAvgExpPenalty();

			deltaPenalty = maxPenalty - penalty;
			if (penalty > maxPenalty) {
				sInterval = systemCapacity;
			} else if (Math.abs(deltaPenalty) > PRECISION) {
				eInterval = systemCapacity;
			} else {
				foundCapacity = true;
			}
		} while (!foundCapacity);
		CapacityOptimizatorOutput output = new CapacityOptimizatorOutput(
				systemCapacity, penaltyOutput);
		return output;
	}

	private DemandEstimatorOutput estimate(){
		capacityEstimator.setInput(getInput().getEstimateInput());
		DemandEstimatorOutput capacityEstimatorOutput = capacityEstimator
				.getOutput();
		return capacityEstimatorOutput;
	}

	private PenaltyOutput getPenalty(List<Double> capacityDemand,
			Double capacity) throws Exception {

		Integer vmsNum = getInput().getVirtualCpuNumber();

		Double x0 = getInput().getX0();
		Double x1 = getInput().getX1();
		Double x2 = getInput().getX2();
		PenaltyCalculatorInput input = new PenaltyCalculatorInput(
				capacityDemand, capacity, vmsNum, x0, x1, x2);
		if (getInput().getPenaltyType() == PenaltyType.EXPONENTIAL)
			input.setCatchAsymptoticException(true);
		penaltyCalculator.setInput(input);
		return penaltyCalculator.getOutput();
	}

	@Override
	protected Set<JFreeChart> createCharts() {
		return penaltyCalculator.createCharts();
	}

	@Override
	public Map<List<CSVRecord>, CSVRecord> createCSVFiles() {
		return penaltyCalculator.createCSVFiles();
	}

}
